package edu.indiana.d2i.vmm.job;

import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
import java.util.Queue;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import edu.indiana.d2i.vmm.SigiriHTRCConfiguration;
import edu.indiana.d2i.vmm.SigiriHTRCJobScheduler;
import edu.indiana.d2i.vmm.policy.VMManagementPolicy;
import edu.indiana.d2i.vmm.vm.BasicHTRCVMController;
import edu.indiana.extreme.sigiri.JobInfoBean;
import edu.indiana.extreme.sigiri.util.ConnectionManager;
import edu.indiana.extreme.sigiri.util.Constants;
import edu.indiana.extreme.sigiri.util.DBUtil;
import edu.indiana.extreme.sigiri.util.SigiriException;

public class SigiriHTRCJobFIFOScheduler implements SigiriHTRCJobScheduler {
	private VMManagementPolicy vmpolicy;
	private ConnectionManager cpm;
	private SigiriHTRCConfiguration configuration;
	private String resourceName;
	private String jobManagerName;
	private int jobSubmissionThreadCount;

	private final int MAX_SCHEDULE_TRY = 3;
	private static final Log logger = LogFactory
			.getLog(SigiriHTRCJobFIFOScheduler.class);

	// private List<JobInfoBean> jobsToBeSubmitted = new
	// ArrayList<JobInfoBean>();
	private Queue<SigiriHTRCJobInProgress> jobQueue = new LinkedList<SigiriHTRCJobInProgress>();

	public SigiriHTRCJobFIFOScheduler(VMManagementPolicy vmpolicy,
			ConnectionManager cpm, SigiriHTRCConfiguration configuration, String resourceName, String jobManagerName,
			int jobSubmissionThreadCount) {
		this.vmpolicy = vmpolicy;
		this.cpm = cpm;
		this.configuration = configuration;
		this.jobSubmissionThreadCount = jobSubmissionThreadCount;
		this.resourceName = resourceName;
		this.jobManagerName = jobManagerName;
	}

	public List<SigiriHTRCJobInProgress> scheduleJob() throws SigiriException {
		if (jobQueue.isEmpty()) {
			List<JobInfoBean> jobsToBeSubmitted = DBUtil.getJobsToBeSubmitted(
					cpm, resourceName, new String[] { jobManagerName },
					jobSubmissionThreadCount);
			for (JobInfoBean jobInfoBean : jobsToBeSubmitted)
				jobQueue.add(new SigiriHTRCJobInProgress(jobInfoBean));
		}

		SigiriHTRCJobInProgress jobInProgress = null;
		try {
			List<SigiriHTRCJobInProgress> runningJobs = new ArrayList<SigiriHTRCJobInProgress>();
			for (int i = 0; i < jobQueue.size(); i++) {
				jobInProgress = jobQueue.poll();
				BasicHTRCVMController vmController = vmpolicy
						.allocateVMs(jobInProgress.jobinfo);
				if (vmController != null) {
					runningJobs
							.add(new SigiriHTRCJobInProgress(
									jobInProgress.jobinfo, vmController,
									SigiriHTRCJobMgrFactory.getJobManager(cpm,
										vmController, jobInProgress.jobinfo, configuration)));
				} else {
					// handle the situation when VM allocation keeps failing
					if (jobInProgress.scheduleInfo.timeScheduled < MAX_SCHEDULE_TRY) {
						jobInProgress.scheduleInfo.timeScheduled++;
						jobQueue.add(jobInProgress);
						logger.info("Prepare to reschedule job "
								+ jobInProgress.jobinfo.getInternalId()
								+ " because no resources can be allocated");
					} else {
						jobInProgress.jobinfo
								.setStatus(Constants.JobStatus.JOB_SUBMISSION_FAILED);
						jobInProgress.jobinfo
								.setErrorDescription("Cannot alloate resources for job "
										+ jobInProgress.jobinfo.getInternalId());
						DBUtil.updateJobTable(cpm, jobInProgress.jobinfo);
						logger.info("No resource is allocated, fail job "
								+ jobInProgress.jobinfo.getInternalId());
					}
				}
			}
			return runningJobs;
		} catch (Exception e) {
			logger.error(e);
			if (jobInProgress != null) {
				jobInProgress.jobinfo
						.setStatus(Constants.JobStatus.JOB_SUBMISSION_FAILED);
				jobInProgress.jobinfo.setErrorDescription(e.getMessage());
				DBUtil.updateJobTable(cpm, jobInProgress.jobinfo);
			}
			return null;
		}
	}
}
